import ash from '@ash.ts/ash';
import { RenderNode } from '../data/nodes';
import { Node, math, v3 } from 'cc';

interface RenderSystemOptions {
    emitStageEvents: boolean;
}

export class RenderSystem extends ash.System {
    private _options: RenderSystemOptions;
    private _nodes: ash.NodeList<RenderNode> | null = null;
    private _stage: Node;
    private _tmp = v3();

    public constructor(container: Node, options: RenderSystemOptions = { emitStageEvents: true }) {
        super();
        this._stage = container;
        this._options = options;
    }

    public addToEngine(engine: ash.Engine): void {
        this._nodes = engine.getNodeList(RenderNode);
        for (let node: RenderNode | null = this._nodes.head; node; node = node.next) {
            this.addToDisplay(node);
        }
        this._nodes.nodeAdded.add(this.addToDisplay);
        this._nodes.nodeRemoved.add(this.removeFromDisplay);
    }

    public removeFromEngine(engine: ash.Engine): void {
        this._nodes.nodeAdded.removeAll();
        this._nodes.nodeRemoved.removeAll();
        this._nodes = null;
    }

    private addToDisplay = (node: RenderNode): void => {
        const { displayObject } = node.display;
        this._stage.addChild(displayObject);
        if (this._options.emitStageEvents) {
            displayObject.emit('addedToStage');
        }
    };

    private removeFromDisplay = (node: RenderNode): void => {
        const { displayObject } = node.display;
        if (this._options.emitStageEvents) {
            displayObject.emit('removedFromStage');
        }
        this._stage.removeChild(displayObject);
    };

    public update(time: number): void {
        for (let node = this._nodes!.head; node; node = node.next) {
            const { display, position } = node;
            display.displayObject.setPosition(this._tmp.set(position.x, position.y, 0));
            display.displayObject.setRotationFromEuler(this._tmp.set(0, 0, math.toDegree(position.rotation)));
        }
    }
}
